package com.ikas.ai.server.module.flow.service;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Maps;
import com.ikas.ai.framework.FlowEventLoop;
import com.ikas.ai.framework.core.model.CalculateParamPair;
import com.ikas.ai.framework.core.model.PageBatchOutput;
import com.ikas.ai.framework.flow.FlowStatus;
import com.ikas.ai.framework.flow.ScoreFlow;
import com.ikas.ai.framework.node.ChainNode;
import com.ikas.ai.framework.node.NodeStatus;
import com.ikas.ai.framework.node.NodeType;
import com.ikas.ai.framework.node.impl.*;
import com.ikas.ai.model.BinaryTuple;
import com.ikas.ai.server.error.BusinessException;
import com.ikas.ai.server.module.appraise.model.AppraiseConstant;
import com.ikas.ai.server.module.appraise.model.AppraiseScene;
import com.ikas.ai.server.module.appraise.service.AppraiseConstantService;
import com.ikas.ai.server.module.assessment.model.po.AssessModel;
import com.ikas.ai.server.module.assessment.model.po.AssessWfResult;
import com.ikas.ai.server.module.assessment.service.AssessModelService;
import com.ikas.ai.server.module.cockpit.model.po.SimplifyRealDataHis;
import com.ikas.ai.server.module.data.model.doris.DorisYxData;
import com.ikas.ai.server.module.data.model.dto.DataTdQueryDto;
import com.ikas.ai.server.module.data.service.DataService;
import com.ikas.ai.server.module.flow.dao.FlowInstanceDAO;
import com.ikas.ai.server.module.flow.enums.LogicSymbol;
import com.ikas.ai.server.module.flow.listener.*;
import com.ikas.ai.server.module.flow.model.*;
import com.ikas.ai.server.module.system.model.User;
import com.ikas.ai.server.module.system.service.UserService;
import com.ikas.ai.utils.CollectionUtil;
import com.ikas.ai.utils.StringUtil;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description: 流程实例service
 *
 * @Author: qi.yilai
 * @Date: 2023/1/17 16:16
 * @Version: 1.0.0
 **/
@Service
public class FlowInstanceService extends ServiceImpl<FlowInstanceDAO, FlowInstance> {
    private static final String DATA = "data";
    private static final String MACHINE_NO = "machineNo";
    private static final String METE_ID = "meteId";
    private static final String METE_CODE = "meteCode";
    private static final String PATH = "path";
    private static final String EXPRESSION = "expression";
    private static final String RESULT_VARIABLE = "resultVariable";
    private static final String OPERATOR = "operator";
    private static final String RISK_NAME = "riskName";
    private static final String LEFT_BRACKET = "(";
    private static final String RIGHT_BRACKET = ")";
    private static final String STRATEGY = "strategy";
    private static final String RESTART_PROB = "restartProb";
    private static final String HAPPEN_PROB = "happenProb";
    private static final String QUERY_TYPE = "queryType";
    private static final String QUERY_CONFIG = "queryConfig";
    private static final String TARGET_COLUMNS = "targetColumns";
    private static final String NUMBER_FUNCTIONS = "numberFunction";
    private static final String STRING_FUNCTIONS = "stringFunction";
    private static final String COMPARE = "compare";
    private static final String NUMBER = "number";
    private static final String COLUMN_TIME_SUFFIX = "_time";
    private static final String CONSTANT_NAME_STR = "constantName";
    private static final String CONSTANT_VALUE_STR = "constantValue";

    private static final String DAYS = "days";
    private static final String HOURS = "hours";
    private static final String MINUTES = "minutes";
    private static final String SECONDS = "seconds";
    private static final String LATEST = "latest";
    private static final String START = "start";
    private static final String END = "end";
    private final String CONSTANT_NAME = "constantName";

    @Autowired
    private FlowService flowService;

    @Autowired
    private FlowNodeService flowNodeService;

    @Autowired
    private FlowNodeExecService flowNodeExecService;

    @Autowired
    private FlowNodeLineService flowNodeLineService;

    @Resource
    private FlowEventLoop flowEventLoop;

    @Autowired
    private MaintainFlowLogListener maintainFlowLogListener;

    @Autowired
    private MaintainFlowListener maintainFlowListener;

    @Autowired
    private MaintainNodeListener maintainNodeListener;

    @Autowired
    private MaintainNodeLogListener maintainNodeLogListener;

    @Autowired
    private DataService dataService;

    @Autowired
    private UserService userService;

    @Autowired
    private AppraiseConstantService appraiseConstantService;

    @Autowired
    private FlowNodeHistoryService flowNodeHistoryService;

    @Resource
    private AssessWfResultListener assessWfResultListener;
    @Resource
    private AssessModelService assessModelService;

    /**
     * 启动评分流程
     *
     * @param flowId
     * @return
     */
    @Transactional
    public FlowInstance startFlow(Long flowId, User user) {
        Flow flow = flowService.getById(flowId);
        if (Objects.isNull(flow)) {
            throw new BusinessException("流程不存在");
        }
        FlowInstance flowInstance = createFlowInstance(flow, user);
        List<FlowNodeExec> execs = createFlowNodeExec(flowInstance);
        createFlowNodeHistory(flowInstance);
        List<FlowNodeLine> lines = flowNodeLineService.getFlowLineByFlowId(flowId);
        AssessModel assessModel = assessModelService.detailAm(flowInstance.getSceneId());
        Map<String, Object> constantsMap = appraiseConstantService.list().stream().collect(Collectors.toMap(AppraiseConstant::getMark, t -> {
            Object result = StringUtil.toNumber(t.getValue());
            if (Objects.isNull(result)) {
                throw new BusinessException("全局常量不支持非数值类型");
            }
            return result;
        }));

        ScoreFlow scoreFlow = new ScoreFlow(flow.getName(), flowId, flowInstance.getId(), flowInstance.getSceneId(), assessModel.getName(), constantsMap);
        scoreFlow.addListener(maintainFlowListener);
        scoreFlow.addListener(maintainFlowLogListener);

        List<FlowNode> nodes = flowNodeService.getNodesByFlowId(flowInstance.getFlowId());
        Map<Long, FlowNode> flowNodeMap = nodes.stream().collect(Collectors.toMap(FlowNode::getId, Function.identity()));
        Map<Long, ChainNode> chainNodeMap = createChainNodes(scoreFlow, execs, flowNodeMap);

        lines.stream().forEach(flowNodeLine -> {
            Long fromNodeId = flowNodeLine.getFromNode();
            Long toNodeId = flowNodeLine.getToNode();
            ChainNode fromChainNode = chainNodeMap.get(fromNodeId);
            ChainNode toChainNode = chainNodeMap.get(toNodeId);
            fromChainNode.appendNext(toChainNode);
            toChainNode.appendPrevious(fromChainNode);
        });
        List<ChainNode> allNodes = new ArrayList<>(chainNodeMap.values());
        scoreFlow.setNodes(new ArrayList<>(chainNodeMap.values()));
        List<ChainNode> filterNodes = allNodes.stream().filter(chainNode -> CollectionUtil.isEmpty(chainNode.getPrevious())).collect(Collectors.toList());
        scoreFlow.setStarts(filterNodes);
        flow.setCurrentFlowInstanceId(flowInstance.getId());
        flowService.updateById(flow);
        flowEventLoop.addFlow(scoreFlow);
        return flowInstance;
    }

    /**
     * 创建flowInstance
     *
     * @param flow
     * @return
     */
    public FlowInstance createFlowInstance(Flow flow, User user) {
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setContent(flow.getContent());
        flowInstance.setFlowId(flow.getId());
        flowInstance.setSceneId(flow.getSceneId());
        flowInstance.setStatus(FlowStatus.CREATED.getStatus().byteValue());
        flowInstance.setCreateTime(new Date());
        flowInstance.setCreator(user.getId());
        save(flowInstance);
        return flowInstance;
    }


    /**
     * 创建flowInstance
     *
     * @param flow
     * @return
     */
    public FlowInstance createFlowInstance(Flow flow, Long userId) {
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setContent(flow.getContent());
        flowInstance.setFlowId(flow.getId());
        flowInstance.setSceneId(flow.getSceneId());
        flowInstance.setStatus(FlowStatus.CREATED.getStatus().byteValue());
        flowInstance.setCreateTime(new Date());
        flowInstance.setCreator(userId);
        save(flowInstance);
        return flowInstance;
    }

    /**
     * 流程运行时节点
     *
     * @param flowInstance
     * @return
     */
    public List<FlowNodeExec> createFlowNodeExec(FlowInstance flowInstance) {
        List<FlowNode> nodes = flowNodeService.getNodesByFlowId(flowInstance.getFlowId());
        if (CollectionUtil.isEmpty(nodes)) {
            throw new BusinessException("流程无法找到有效节点");
        }

        List<FlowNodeExec> execs = Lists.newArrayList();
        nodes.stream().forEach(node -> {
            FlowNodeExec flowNodeExec = new FlowNodeExec();
            flowNodeExec.setSceneId(flowInstance.getSceneId());
            flowNodeExec.setFlowId(flowInstance.getFlowId());
            flowNodeExec.setFlowInstanceId(flowInstance.getId());
            flowNodeExec.setNodeId(node.getId());
            flowNodeExec.setX6InstanceId(node.getX6InstanceId());
            flowNodeExec.setStatus(NodeStatus.CREATED.getStatus().byteValue());
            flowNodeExec.setCreateTime(new Date());
            execs.add(flowNodeExec);
        });
        flowNodeExecService.saveBatch(execs);
        return execs;
    }

    private void createFlowNodeHistory(FlowInstance flowInstance) {
        List<FlowNode> nodes = flowNodeService.getNodesByFlowId(flowInstance.getFlowId());
        List<FlowNodeHistory> histories = nodes.stream().map(flowNode -> {
            FlowNodeHistory flowNodeHistory = new FlowNodeHistory();
            flowNodeHistory.setNodeId(flowNode.getId());
            flowNodeHistory.setFlowId(flowInstance.getFlowId());
            flowNodeHistory.setFlowInstanceId(flowInstance.getId());
            flowNodeHistory.setSceneId(flowInstance.getSceneId());
            flowNodeHistory.setName(flowNode.getName());
            flowNodeHistory.setX6InstanceId(flowNode.getX6InstanceId());
            flowNodeHistory.setNodeType(flowNode.getNodeType());
            flowNodeHistory.setNodeTypeName(flowNode.getNodeTypeName());
            flowNodeHistory.setParamValue(flowNode.getParamValue());
            return flowNodeHistory;
        }).collect(Collectors.toList());
        flowNodeHistoryService.saveBatch(histories);
    }

    public Map<Long, ChainNode> createChainNodes(ScoreFlow scoreFlow, List<FlowNodeExec> execs, Map<Long, FlowNode> nodeMap) {
        Map<Long, ChainNode> chainNodeMap = Maps.newHashMap();
        execs.forEach(flowNodeExec -> {
            FlowNode flowNode = nodeMap.get(flowNodeExec.getNodeId());
            ChainNode chainNode = doCreateChainNode(scoreFlow, flowNodeExec, flowNode);
            chainNodeMap.put(flowNodeExec.getNodeId(), chainNode);
        });
        return chainNodeMap;
    }

    /**
     * 创建ChainNode
     *
     * @param flowNodeExec
     * @param flowNode
     * @return
     */
    public ChainNode doCreateChainNode(ScoreFlow scoreFlow, FlowNodeExec flowNodeExec, FlowNode flowNode) {
        String nodeTypeName = flowNode.getNodeTypeName();
        NodeType nodeType = NodeType.of(nodeTypeName);
        if (Objects.isNull(nodeType)) {
            throw new BusinessException(String.format("不支持%s节点类型", nodeTypeName));
        }
        ChainNode chainNode;
        AbstractNode node;
        switch (nodeType) {
            case DATA:
                JSONObject value = JSON.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(value)) {
                    throw new BusinessException(String.format("【%s】测点配置不能为空", flowNode.getName()));
                }
                node = new DataNode(scoreFlow, () -> {
                    JSONObject meteJsonObject = value.getJSONObject(DATA);
                    String machineNo = meteJsonObject.getString(MACHINE_NO);
                    String meteId = meteJsonObject.getString(METE_ID);
                    String meteCode = meteJsonObject.getString(METE_CODE);
                    DorisYxData data = dataService.getLatestOneDataMete(machineNo, meteCode, Long.getLong(meteId));
                    JSONObject result = new JSONObject();
                    if (Objects.isNull(data)) {
                        result.put(meteCode, null);
                        result.put(meteCode + COLUMN_TIME_SUFFIX, null);
                    } else {
                        Object dataValue = StringUtil.toNumber(data.getValue());
                        if (Objects.isNull(dataValue)) {
                            throw new BusinessException("数据节点非数值类型");
                        }
                        result.put(meteCode, dataValue);
                        Date reportTime = data.getReportTime();
                        result.put(meteCode + COLUMN_TIME_SUFFIX,
                                reportTime==null ? null : reportTime.getTime());
                    }
                    return result;
                }, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case MODEL_CALC:
                JSONObject paramValue = JSON.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(paramValue)) {
                    throw new BusinessException(String.format("【%s】模型计算配置不能为空", flowNode.getName()));
                }
                String path = paramValue.getString(PATH);
                node = new ModelCalcNode(scoreFlow, path, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case MATH_CALC:
                JSONArray jsonArray = JSON.parseArray(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(jsonArray)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                List<CalculateParamPair> calculateParamPairs = Lists.newArrayList();
                for (int i = 0; i < jsonArray.size(); i ++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    String expression = jsonObject.getString(EXPRESSION);
                    String resultVariable = jsonObject.getString(RESULT_VARIABLE);
                    CalculateParamPair calculateParamPair = new CalculateParamPair(expression, resultVariable);
                    calculateParamPairs.add(calculateParamPair);
                }
                if (CollectionUtil.isEmpty(calculateParamPairs)) {
                    throw new BusinessException(String.format("【%s】公式配置不能为空", flowNode.getName()));
                }
                node = new MathCalculateNode(scoreFlow, calculateParamPairs, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case PREDICT:
                JSONObject predictValue = JSON.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(predictValue)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                List<String> expressions = predictValue.getJSONArray(EXPRESSION).stream().map(exp -> LEFT_BRACKET + exp.toString() + RIGHT_BRACKET).collect(Collectors.toList());
                String operator = predictValue.getString(OPERATOR);

                String joinExpression;
                if (StringUtils.isEmpty(operator)) {
                    joinExpression = expressions.get(0);
                } else {
                    LogicSymbol logicSymbol = LogicSymbol.of(operator);
                    joinExpression = StringUtils.join(expressions, logicSymbol.getSymbol());
                }
                if (StringUtils.isEmpty(joinExpression)) {
                    throw new BusinessException(String.format("【%s】判断表达式不能为空", flowNode.getName()));
                }
                node = new PredictNode(scoreFlow, joinExpression, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case END:
                JSONObject endValue = JSON.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(endValue)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                String riskName = endValue.getString(RISK_NAME);
                String strategy = Objects.toString(endValue.getString(STRATEGY), "");
                Double restartProb = endValue.getDouble(RESTART_PROB);
                restartProb = restartProb==null?0.0:restartProb;
                Double happenProb = endValue.getDouble(HAPPEN_PROB);
                happenProb = happenProb==null?0.0:happenProb;
                node = new EndNode(scoreFlow, riskName, strategy, restartProb, happenProb, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case BATCH_DATA:
                JSONObject batchDataValue = JSON.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(batchDataValue)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                JSONObject meteData = batchDataValue.getJSONObject(DATA);
                int queryType = batchDataValue.getInteger(QUERY_TYPE);
                JSONObject queryConfig = batchDataValue.getJSONObject(QUERY_CONFIG);
                node = new BatchDataNode(scoreFlow, query -> {
                    DataTdQueryDto dataTdQueryDto = new DataTdQueryDto();
                    dataTdQueryDto.setMeteCode(query.getMeteData().getString(METE_CODE));
                    dataTdQueryDto.setMachineNo(query.getMeteData().getString(MACHINE_NO));
                    dataTdQueryDto.setMeteId(Long.getLong(query.getMeteData().getString(METE_ID)));
                    if (query.getQueryType() == 1) {
                        // 1=按时长取数据
                        DataTdQueryDto.LatestDateType latestDateType = new DataTdQueryDto.LatestDateType();
                        latestDateType.setDay(query.getQueryConfig().getInteger(DAYS));
                        latestDateType.setHour(query.getQueryConfig().getInteger(HOURS));
                        latestDateType.setMinute(query.getQueryConfig().getInteger(MINUTES));
                        latestDateType.setSecond(query.getQueryConfig().getInteger(SECONDS));
                        dataTdQueryDto.setLatestDateType(latestDateType);
                    } else if (query.getQueryType() == 2) {
                        // 2=按数据量取数
                        DataTdQueryDto.LatestNumType latestNumType = new DataTdQueryDto.LatestNumType();
                        latestNumType.setNum(query.getQueryConfig().getIntValue(LATEST));
                        dataTdQueryDto.setLatestNumType(latestNumType);
                    } else {
                        // 3=按时间段取数
                        DataTdQueryDto.DateTimeType dateTimeType = new DataTdQueryDto.DateTimeType();
                        Date startDate = new Date(query.getQueryConfig().getLongValue(START));
                        Date endDate = new Date(query.getQueryConfig().getLongValue(END));
                        dateTimeType.setStartDate(query.getQueryConfig().getLong(START) == null ?
                                null:DateUtil.format(startDate, "yyyy-MM-dd HH:mm:ss"));
                        dateTimeType.setEndDate(query.getQueryConfig().getLong(END) == null ?
                                null:DateUtil.format(endDate, "yyyy-MM-dd HH:mm:ss"));
                        dataTdQueryDto.setDateTimeType(dateTimeType);
                    }
                    PageInfo<SimplifyRealDataHis> pageInfo = dataService.getDataMetesFromTd(dataTdQueryDto);
                    PageBatchOutput pageBatchOutput = new PageBatchOutput();
                    pageBatchOutput.setTotal(pageInfo.getTotal());
                    pageBatchOutput.setPage(query.getPage());
                    pageBatchOutput.setPageCount(pageInfo.getPages());
                    pageBatchOutput.setPageSize(query.getPageSize());
                    if (CollectionUtil.isEmpty(pageInfo.getList())) {
                        pageBatchOutput.setDataMap(Maps.newHashMap());
                    } else {
                        List<SimplifyRealDataHis> list = pageInfo.getList();
                        Map<String, List<String>> dataMap = Maps.newHashMap();
                        dataMap.put(dataTdQueryDto.getMeteCode(), list.stream().map(SimplifyRealDataHis::getValue).collect(Collectors.toList()));
                        dataMap.put(dataTdQueryDto.getMeteCode() + COLUMN_TIME_SUFFIX, list.stream().map(t -> String.valueOf(t.getIdtime())).collect(Collectors.toList()));
                        pageBatchOutput.setDataMap(dataMap);
                    }
                   return pageBatchOutput;
                }, queryType, queryConfig, meteData, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case MATCH_STATISTICS:
                JSONObject statisticsData = JSONObject.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(statisticsData)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                JSONArray targetColumns = statisticsData.getJSONArray(TARGET_COLUMNS);
                List<String> numberFunctions = statisticsData.getJSONArray(NUMBER_FUNCTIONS).toJavaList(String.class);
                List<String> stringFunctions = statisticsData.getJSONArray(STRING_FUNCTIONS).toJavaList(String.class);
                if (CollectionUtil.isEmpty(numberFunctions) && CollectionUtil.isEmpty(stringFunctions)) {
                    throw new BusinessException(String.format("【%s】统计方法不能为空", flowNode.getName()));
                }
                node = new MathStatisticsNode(scoreFlow, targetColumns, numberFunctions, stringFunctions, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case AND_PREDICT:
                node = new AndPredictNode(scoreFlow, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case OR_PREDICT:
                node = new OrPredictNode(scoreFlow, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case COMPOSITE_PREDICT:
                JSONObject predictData = JSONObject.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(predictData)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                String compare = predictData.getString(COMPARE);
                int number = predictData.getInteger(NUMBER);
                node = new CompositePredictNode(scoreFlow, compare, number, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            case BUSINESS_CONSTANT:
                //业务常量组件
                JSONObject constantParams = JSONObject.parseObject(flowNode.getParamValue());
                if (CollectionUtil.isEmpty(constantParams)) {
                    throw new BusinessException(String.format("【%s】配置不能为空", flowNode.getName()));
                }
                JSONObject constantJson = constantParams.getJSONObject(DATA);
                Optional.ofNullable(constantJson).orElseThrow(()->new BusinessException("未获取到【data】内容信息"));
                JSONObject businessConstantParams = new JSONObject();
                businessConstantParams.put(constantJson.getString(CONSTANT_NAME_STR), constantJson.getDouble(CONSTANT_VALUE_STR));
                node = new BusinessConstantNode(scoreFlow, businessConstantParams, flowNode.getId(), flowNodeExec.getId(), flowNode.getName());
                break;
            default:
                throw new BusinessException("暂时不支持创建其他类型节点");
        }
        node.addListener(maintainNodeLogListener);
        node.addListener(maintainNodeListener);
        chainNode = new ChainNode(node);
        return chainNode;
    }

    private void handleBusinessConstantParams(JSONObject params) {
        if (params.containsKey("constantName")) {
            params.put(params.getString("constantName"), params.get("constantValue"));
        }
    }

    /**
     * 暂停流程
     *
     * @param flowInstanceId
     * @return
     */
    public boolean stopFlow(Long flowInstanceId) {
        FlowInstance flowInstance = getById(flowInstanceId);
        if (Objects.isNull(flowInstance)) {
            throw new BusinessException("流程不存在");
        }
        boolean flag = flowEventLoop.isExecuting(flowInstanceId);
        if (!flag) {
            throw new BusinessException("流程未在运行中");
        }
        return flowEventLoop.stopFlow(flowInstanceId);
    }


    /**
     * 开始流程任务
     *
     * @param awfResult 评价模型结果对象
     * @return
     */
    public BinaryTuple<FlowInstance, ScoreFlow> startFlowTask(AssessWfResult awfResult) {
        Flow flow = flowService.getFlowBySceneId(awfResult.getModelId());

        if (Objects.isNull(flow)) {
            throw new BusinessException("流程不存在");
        }
        FlowInstance flowInstance = createFlowInstance(flow, awfResult.getCreator());
        List<FlowNodeExec> execs = createFlowNodeExec(flowInstance);
        createFlowNodeHistory(flowInstance);
        List<FlowNodeLine> lines = flowNodeLineService.getFlowLineByFlowId(flow.getId());

        Map<String, Object> constantsMap = appraiseConstantService.list().stream()
                .collect(Collectors.toMap(AppraiseConstant::getMark, t -> {
            Object result = StringUtil.toNumber(t.getValue());
            if (Objects.isNull(result)) {
                throw new BusinessException("全局常量不支持非数值类型");
            }
            return result;
        }));
        ScoreFlow scoreFlow = new ScoreFlow(flow.getName(), flow.getId(), flowInstance.getId(),
                flowInstance.getSceneId(), awfResult.getModelName(), constantsMap);
        scoreFlow.addListener(maintainFlowListener);
        scoreFlow.addListener(maintainFlowLogListener);
        scoreFlow.addListener(assessWfResultListener);

        List<FlowNode> nodes = flowNodeService.getNodesByFlowId(flowInstance.getFlowId());
        Map<Long, FlowNode> flowNodeMap = nodes.stream().collect(Collectors.toMap(FlowNode::getId, Function.identity()));
        Map<Long, ChainNode> chainNodeMap = createChainNodes(scoreFlow, execs, flowNodeMap);

        lines.stream().forEach(flowNodeLine -> {
            Long fromNodeId = flowNodeLine.getFromNode();
            Long toNodeId = flowNodeLine.getToNode();
            ChainNode fromChainNode = chainNodeMap.get(fromNodeId);
            ChainNode toChainNode = chainNodeMap.get(toNodeId);
            fromChainNode.appendNext(toChainNode);
            toChainNode.appendPrevious(fromChainNode);
        });
        List<ChainNode> allNodes = new ArrayList<>(chainNodeMap.values());
        scoreFlow.setNodes(new ArrayList<>(chainNodeMap.values()));
        List<ChainNode> filterNodes = allNodes.stream().filter(chainNode -> CollectionUtil.isEmpty(chainNode.getPrevious())).collect(Collectors.toList());
        scoreFlow.setStarts(filterNodes);
        return BinaryTuple.of(flowInstance, scoreFlow);
    }
}
